home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #15
/
Monster Media Number 15 (Monster Media)(July 1996).ISO
/
math
/
alged34.zip
/
ALGEDSRC.ZIP
/
ALGRAPH.C
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-06
|
26KB
|
839 lines
/*--------------------------------------------------------------------
Alged: Algebra Editor henckel@vnet.ibm.com
Copyright (c) 1994 John Henckel
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies.
*/
#include "alged.h"
#include <graphics.h>
#include <dos.h>
/*
xs is size
xo is origin (left edge)
xz is maxx/xs (number of pixels per unit length)
np is number of points
*/
static node* defx; // default fx
static double xs=0,xo,ys,yo,xz,yz,np,ts,to;
static double err,step;
static node ivar[10]; /* independent variables */
static int gm,polar,nv,rain,fill;
static int co1,co2,co3; /* colors */
extern char gdriver[80];
extern int gmode,psz,pst;
/*-----------------------------------------------------------------
Palette variables
*/
typedef struct { unsigned char r,g,b; } palen;
static palen pal[480];
static int v256;
/*-----------------------------------------------------------------
3d variables
*/
static int in3d,vix=1;
static double focalen = 5, cont;
static double cx,cy,cz; // camera location
static double ax,ay,az; // camera orientation
static double m[4][4]; // transform matrix
extern double lightx,lighty,lightz; // see algfile.c
#define EPS 1E-200
/* These are the zooming ratios.... rr1 is (1-rr)/2 */
#define rr 0.7
#define rr1 0.15
#define tt (M_PI/72)
typedef struct { // save previous row data for polyfill
short x2,y2; // screen location
} point2d;
typedef struct { // save previous row data for polyfill
double x,y,z; // 3d location
} point3d;
/*-----------------------------------------------------------------
find var
*/
double getvar(char *name) { int i;
for (i=0; i<nv; ++i)
if (!strcmp(name,ivar[i].name)) return ivar[i].value;
return 0.0;
}
/*-----------------------------------------------------------------
min and max
*/
double mind(double x, double y) {
return min(x,y);
}
double maxd(double x, double y) {
return max(x,y);
}
/*-----------------------------------------------------------------
gray_palette
*/
void gray_palette()
{
int i;
for (i=0; i<120; ++i) pal[i].r = pal[i].g = pal[i].b = i/2+3;
for (i=120; i<240; ++i) pal[i].r = pal[i].g = pal[i].b = 123-i/2;
for (i=0; i<240; ++i) pal[i+240] = pal[i];
}
/*-----------------------------------------------------------------
wave_palette
*/
#define cwav(r) 31*(sin((i+r)*M_PI/120)+1)
// #define cwav(r) max(min(abs((i+r)%240-120)-40,40),0)*63/40
void wave_palette()
{
int i;
for (i=0; i<240; ++i) {
pal[i].r = cwav(0);
pal[i].g = cwav(80);
pal[i].b = cwav(160);
}
for (i=0; i<240; ++i) pal[i+240] = pal[i];
}
/* positive modulo */
long pmod(long x,long y) {
if (y==0) return x;
if (y<0) y=-y;
x = x % y;
if (x < 0) x += y;
return x;
}
/*-----------------------------------------------------------------
set_palette - set the top 240 colors of the vga palette
*/
void set_palette(int i)
{
struct REGPACK r;
i = pmod(i,240);
r.r_ax = 0x1012;
r.r_bx = 0x0010; /* start */
r.r_cx = 0x00F0; /* length */
r.r_es = FP_SEG(pal);
r.r_dx = FP_OFF(pal) + i*sizeof*pal;
intr(0x10, &r);
}
/*-----------------------------------------------------------------
eval - return value of p, using ivar
*/
double eval(node *p) {
double u,v;
if (!p) return 0;
switch (p->kind) {
case VAR: return getvar(p->name);
case NUM: return p->value;
case ADD: return eval(p->lf) + eval(p->rt);
case SUB: return eval(p->lf) - eval(p->rt);
case MUL: return eval(p->lf) * eval(p->rt);
case DIV:
v = eval(p->rt);
if (fabs(v)<err) return 0;
return eval(p->lf) / v;
case EXP:
u = eval(p->lf);
v = eval(p->rt);
if (u<err && !whole(v)) modf(v,&v); // result would be imaginary
if (fabs(v * log10(fabs(u)+1)) > 300) return 0;
if (!v) return 0;
return pow(u,v);
case EQU: return mind(eval(p->lf),eval(p->rt));
case FUN:
if (!strcmp(p->name,"sin")) return sin(eval(p->lf));
if (!strcmp(p->name,"cos")) return cos(eval(p->lf));
if (!strcmp(p->name,"tan")) return tan(eval(p->lf));
if (!strcmp(p->name,"asin")) {
v = eval(p->lf);
if (v>1 || v<-1) return 0;
return asin(v);
}
if (!strcmp(p->name,"acos")) {
v = eval(p->lf);
if (v>1 || v<-1) return 0;
return acos(v);
}
if (!strcmp(p->name,"atan")) return atan(eval(p->lf));
if (!strcmp(p->name,"sinh")) return sinh(eval(p->lf));
if (!strcmp(p->name,"cosh")) return cosh(eval(p->lf));
if (!strcmp(p->name,"tanh")) return tanh(eval(p->lf));
if (!strcmp(p->name,"rand")) return eval(p->lf)*rand()/RAND_MAX;
if (!strcmp(p->name,"sign")) {
v = eval(p->lf);
return v<0?-1:v>0?1:0;
}
if (!strcmp(p->name,"ln")) {
v = eval(p->lf);
if (v<=0) return 0;
return log(v);
}
if (!strcmp(p->name,"log")) {
v = eval(p->lf);
if (v<=0) return 0;
return log10(v);
}
if (!strcmp(p->name,"abs")) return fabs(eval(p->lf));
if (!strcmp(p->name,"r") && p->nump==2) return hypot(eval(p->lf),eval(p->rt));
if (!strcmp(p->name,"min") && p->nump==2) return mind(eval(p->lf),eval(p->rt));
if (!strcmp(p->name,"max") && p->nump==2) return maxd(eval(p->lf),eval(p->rt));
if (!strcmp(p->name,"atan2") && p->nump==2) {
u = eval(p->lf);
v = eval(p->rt);
if (v==0 && u==0) return 0;
return atan2(u,v);
}
if (!strcmp(p->name,"mod") && p->nump==2) return fmod(eval(p->lf),eval(p->rt));
default: return 0;
}
}
/*-----------------------------------------------------------------
reset window, use a weird number for xs to get better safety
*/
void resetx() { int i;
xs = 7-M_PI/13; ys = xs*0.75;
xo=xs/-2; yo=ys/-2; np=25;
err = pow(10,-sigdig); step=1.0;
for (i=0; i<10; ++i) ivar[i].value = 0;
cz=2.5; cy=2.58819; cx=9.33013; // camera location
ax=.261799; ay=M_PI/2+ax; az=0; // camera orientation
ts=xs; to=xo; vix=1;
co1=15; co2=2; co3=1; rain=0;
focalen=5; fill=0; polar=0; cont=0.2;
}
/*-----------------------------------------------------------------
add a new var
*/
void addvar(char *name) { int i;
for (i=0; i<nv; ++i) if (!strcmp(name,ivar[i].name)) break;
if (i==nv && nv<10) strcpy(ivar[nv++].name,name);
}
/*-----------------------------------------------------------------
look for the first var in p
*/
void setivar(node *p) { int i;
if (p->kind==VAR) addvar(p->name);
for (i=0; i<p->nump; ++i) setivar(p->parm[i]);
}
/*-----------------------------------------------------------------
show help file
*/
void showghelp() {
FILE *f;
int i,c=0;
static char s[85];
strcpy(s,"alged");
strcat(s,lang);
strcat(s,".hlq");
f = fopen(s,"r");
if (!f) { printf(msg[16],s);
pause; return;
}
restorecrtmode();
if (ti.screenheight>25) textmode(64); /* ega 43 line mode */
textattr(norm);
clrscr();
i = ti.screenheight-1;
while (!feof(f)) {
printf(fgets(s,80,f));
if (!--i) {
i = ti.screenheight-4;
while (!(c=getch()));
if (c==27) break;
}
}
if (c!=27) getch();
fclose(f);
setgraphmode(gmode);
}
/*-----------------------------------------------------------------
rotate vars starting at index i
*/
void rot_var(int i) {
node t;
if (nv < 2) return;
t = ivar[i];
t.value = 0;
for (++i; i<nv; ++i) {
ivar[i-1] = ivar[i];
ivar[i-1].value = 0;
}
ivar[nv-1] = t;
strcpy(defx->name,ivar[0].name); // default fx is ivar[0]
}
/*-----------------------------------------------------------------
handle key
*/
void handlekey(char c) {
switch (c) {
case ';': showghelp(); break; /* F1 */
case 72: yo+=ys/4; break;
case 75: xo-=xs/4; break;
case 77: xo+=xs/4; break;
case 80: yo-=ys/4; break;
case 82: xo+=xs*rr1; yo+=ys*rr1; xs*=rr; ys*=rr; break;
case 83: xs/=rr; ys/=rr; xo-=xs*rr1; yo-=ys*rr1; break;
case 71: np/=rr; break;
case 79: if (np>6) np*=rr; break;
case 73: ys/=rr; yo-=ys*rr1; break;
case 81: yo+=ys*rr1; ys*=rr; break;
case 'd': resetx(); break;
case 'a': polar = ++polar%3;
if (!in3d && polar>1) polar=0; break;
case 'q': ivar[1].value -= step; break;
case 'w': ivar[2].value -= step; break;
case 'e': ivar[3].value -= step; break;
case 'r': ivar[4].value -= step; break;
case 't': ivar[5].value -= step; break;
case 'y': ivar[6].value -= step; break;
case 'u': ivar[7].value -= step; break;
case 'i': ivar[8].value -= step; break;
case 'o': ivar[9].value -= step; break;
case 'p': step *= sqrt(.1); break;
case '1': ivar[1].value += step; break;
case '2': ivar[2].value += step; break;
case '3': ivar[3].value += step; break;
case '4': ivar[4].value += step; break;
case '5': ivar[5].value += step; break;
case '6': ivar[6].value += step; break;
case '7': ivar[7].value += step; break;
case '8': ivar[8].value += step; break;
case '9': ivar[9].value += step; break;
case '0': step /= sqrt(.1); break;
case 'V':
if (in3d) rot_var(1);
if (++vix >= nv) vix = 1; // variable index counter
if (!in3d || vix==1) rot_var(0);
break;
}
}
/*-----------------------------------------------------------------
MAIN GRAPH ROUTINE
fx is an optional scaling function for the x coordinate, and fy
is a required function for the y coord. In case of polar coordinates
the x is theta and the y is radius.
*/
#define rline(x,y,a,b) line(xz*(x-xo),yz*(ys+yo-(y)),xz*(a-xo),yz*(ys+yo-(b)))
void graph(node *fx,node *fy) {
int gd=DETECT,c,i;
double t,x1,y1,x2,y2,z;
char ss[60];
nv=0;
strcpy(ivar[1].name,"y");
setivar(fy);
if (fx) setivar(fx);
if (!nv) addvar("x");
if (!defx) defx = newvar("x");
if (!fx) {
fx = defx;
strcpy(defx->name,ivar[0].name);
}
if (!xs) {
if (*gdriver && *gdriver!='*') { // customized bgi file!
gd=installuserdriver(gdriver,NULL);
gm=gmode;
if (!!(i=graphresult())) {
printf(msg[17],i,grapherrormsg(i));
pause;
return;
}
}
initgraph(&gd,&gm,"");
if (!!(i=graphresult())) {
printf(msg[17],i,grapherrormsg(i));
pause;
return;
}
if (!*gdriver) gmode = gm; // auto detect and auto mode
if (gm != gmode) setgraphmode(gmode);
resetx();
if (psz<2) { psz = getpalettesize()-1; pst=1; }
v256 = (psz+pst>17 || getpalettesize()>17);
}
else setgraphmode(gmode);
while (1) {
xz = getmaxx()/xs;
yz = getmaxy()/ys;
if (in3d && graph3d(fx,fy)) break;
cleardevice();
// setlinestyle(DOTTED_LINE,0,1);
setcolor(co2);
rline(xo,0,xo+xs,0);
rline(0,yo,0,yo+ys);
rline(.1,1,-.1,1);
rline(.1,-1,-.1,-1);
rline(1,.1,1,-.1);
rline(-1,.1,-1,-.1);
rline(.1,2,-.1,2);
rline(.1,-2,-.1,-2);
rline(2,.1,2,-.1);
rline(-2,.1,-2,-.1);
rline(1.1,1,.9,1);
rline(1,1.1,1,.9);
rline(-1.1,1,-.9,1);
rline(-1,1.1,-1,.9);
rline(1.1,-1,.9,-1);
rline(1,-1.1,1,-.9);
rline(-1.1,-1,-.9,-1);
rline(-1,-1.1,-1,-.9);
// setlinestyle(SOLID_LINE,0,1);
setcolor(co1);
sprintf(ss,"%g ",yo+ys); outtextxy(getmaxx()/2,0,ss);
sprintf(ss,"%g ",yo ); outtextxy(getmaxx()/2,getmaxy()-8,ss);
sprintf(ss,"%g ",xo+xs); outtextxy(getmaxx()-50,getmaxy()/2,ss);
sprintf(ss,"%g ",xo ); outtextxy(0,getmaxy()/2,ss);
sprintf(ss,"%s %g ",msg[18],step);
if (polar) strcat(ss,msg[19]);
strcat(ss,msg[36]);
outtextxy(0,0,ss);
sprintf(ss,"%s =[%8.4f %8.4f] ",ivar[0].name,xo,xo+xs);
outtextxy(0,10,ss);
for (i=1; i<nv; ) {
sprintf(ss,"%s = %8.4f ",ivar[i].name,ivar[i].value);
outtextxy(0,++i*10,ss);
}
srand(2);
for (t=xo; t<=xs+xo; t+=xs/np) {
ivar[0].value = t;
if (polar) ivar[0].value = (2*(t-xo)/xs-1)*M_PI;
x2 = eval(fx);
y2 = eval(fy);
if (polar) {
z = x2;
x2 = y2*cos(z);
y2 = y2*sin(z);
}
x2 = xz*(x2-xo);
y2 = yz*(ys+yo-y2);
if (x2>30000) x2=30000;
if (x2<-30000) x2=-30000;
if (y2>30000) y2=30000;
if (y2<-30000) y2=-30000;
if (t!=xo) line(x1,y1,x2,y2);
x1=x2; y1=y2;
if (kbhit()) break;
}
c = getch();
while (kbhit()) c = getch();
if (c==27) break; /* escape */
in3d=1;
if (c=='g' && graph3d(fx,fy)) break; /* 3d graphics */
in3d=0;
handlekey(c);
}
restorecrtmode();
if (ti.screenheight>25) textmode(64); /* ega 43 line mode */
}
/*-----------------------------------------------------------------
compute matrix
*/
static void computematrix() {
double sa,sb,sc,ca,cb,cc; int i;
// Precompute some sines and cosines
ca=cos(ax); sa=sin(ax);
cb=cos(ay); sb=sin(ay);
cc=cos(az); sc=sin(az);
// Compute rotation transformation
m[0][0]=cb*cc-sb*sa*sc; /* R = Rb.Ra.Rc */
m[0][1]=ca*sc;
m[0][2]=sb*cc+cb*sa*sc;
m[1][0]=-cb*sc-sb*sa*cc;
m[1][1]=ca*cc;
m[1][2]=-sb*sc+cb*sa*cc;
m[2][0]=-sb*ca;
m[2][1]=-sa;
m[2][2]=cb*ca;
// Compute translation transformation
for (i=0; i<3; ++i) m[i][3]=-cx*m[i][0]-cy*m[i][1]-cz*m[i][2];
for (i=0; i<4; ++i) m[3][i]=m[2][i]/focalen;
}
static void view3d(double dx,double dy,double dz,double *xx,double *yy) {
double x,y,z,t;
x = dx*m[0][0] + dy*m[0][1] + dz*m[0][2] + m[0][3];
y = dx*m[1][0] + dy*m[1][1] + dz*m[1][2] + m[1][3];
z = dx*m[3][0] + dy*m[3][1] + dz*m[3][2] + m[3][3];
// Check if the point is behind the visual cone.
t = hypot(x,y)/5; // 5 is slope of the cone
if (z < t) z=t; // push point to the cone
if (z < EPS) z=EPS; // don't divide by zero
*xx = x/z;
*yy = y/z;
}
/*-----------------------------------------------------------------
move camera forward or backward
*/
void movecam(double spd) {
double y,d;
y=sin(ax);
d=cos(ax);
cz+=spd*cos(ay)*d;
cy-=spd*y;
cx-=spd*sin(ay)*d;
}
/*-----------------------------------------------------------------
rotate camera - with constant radius v=-1,1 vertically or 3,5 horiz
*/
void spincam(int v) {
double r,t,a;
r = hypot(cx,cz);
if (v<2) { // vertical
t = atan2(cy,r) + tt*v;
if (fabs(t)>M_PI/2) return;
a = hypot(r,cy);
cy = sin(t)*a;
a *= cos(t)/r;
cx *= a; cz *= a;
ax+=tt*v;
}
else { // horizontal
t = atan2(cz,cx);
t+=tt*(v-4);
cx=cos(t)*r;
cz=sin(t)*r;
ay+=tt*(v-4);
}
}
/*-----------------------------------------------------------------
These are some macros to ease 3D vector computation
*/
#define diff3d(A,B,C) { \
(A##x) = (B##x) - (C##x); \
(A##y) = (B##y) - (C##y); \
(A##z) = (B##z) - (C##z); \
}
#define cross3d(A,B,C) { \
(A##x) = (B##y)*(C##z) - (C##y)*(B##z); \
(A##y) = (B##z)*(C##x) - (C##z)*(B##x); \
(A##z) = (B##x)*(C##y) - (C##x)*(B##y); \
}
#define len3d(A) hypot(A##x,hypot(A##y,A##z))
/*-----------------------------------------------------------------
compute brightness of a triangle using light source
return a positive number less than range/2
*/
int light_source(double px, double py, double pz,point3d *save,int range)
{
static double ax,ay,az,bx,by,bz,cx,cy,cz,t;
/* get two vectors: a,b in the plane, and compute cross product */
diff3d(a,p,save[0].);
diff3d(b,p,save[1].);
cross3d(c,a,b); /* c = a x b, normal to the plane */
t = len3d(c);
if (t<EPS) cy=1;
else { cx/=t; cy/=t; cz/=t; } /* make unit vector */
t = cx*lightx + cy*lighty + cz*lightz; /* dot product = cos theta */
if (polar) t=-t; // why?
if (range>16) range/=2; // if large spectrum, use half
if (t>0) return range*((1-cont)*t+cont);
return range*(cont*t+cont);
}
#define rline3(a,b,c,u,v,w) \
view3d(a,b,c,&x,&y); \
view3d(u,v,w,&z,&yy); \
rline(x,y,z,yy)
/*------------------------------------------------------------------
3D Graphics
This draws a 3d surface function where the altitude is z = fz,
and x is the first variable found in fx and y is fy (if specified)
or if fy is NULL then y is the second variable found in fz. If
there are insufficient variables in fz, then a zero is returned.
Implementation: z is fz, x is ivar[0],y is fy or ivar[1].
P.S. I changed my mind in the middle of doing this. Now y is fz,
a function of x and z, and fy is unused. I did this because y is the
vertical axis. Thus the code is confusing at places.
Actually, I decided to make x=fy, so that I can make a bagel in polar
mode. So the code is very confusing at places.
*/
int graph3d(node *fy,node *fz) {
int c,i,j,mm=0; // mm 0=norm, 1=camera, 2=grid
static double x,y,z,xx,yy,cc,zz,x2,y2;
static point2d *save2 = 0;
static point3d *save3 = 0;
static struct { int x1,y1,x2,y2,x3,y3,x4,y4; } f; // used by fillpoly
static char ss[60];
static int np=0,onp=0;
static double zmag,uo,us;
static short fillpat[] = { 10,11,3,8,7,900 };
int spd=0, palrot=0, palix=0, cmn,cmx;
double ca=1;
int pp=0, palst=pst, palsz=psz;
while (1) {
if (spd) { // speedy mode (cleardevice is very slow)
delay(50);
setcolor(0);
rline3(-2,0,0,2,0,0); // erase axes (draw black)
rline3(0,-2*zmag,0,0,2*zmag,0);
rline3(0,0,-2,0,0,2);
rline3(2.2,0,0,2,0,-0.1); // arrows
rline3(2.2,0,0,2,0, 0.1);
rline3(0,0,2.2,-0.1,0,2);
rline3(0,0,2.2, 0.1,0,2);
rline3(0,2.2*zmag,0,-0.1,2*zmag,0);
rline3(0,2.2*zmag,0, 0.1,2*zmag,0);
}
// if (polar) { uo=-M_PI; us=-2*uo; to=uo/2; ts=us/2; }
if (!np) { // reset defaults
zmag=1; us=ts; uo=to; np=25;
ca=1.0; palrot=palix=0;
}
computematrix();
xz = getmaxx()/xs;
yz = getmaxy()/ys;
if (np<3) np = 3;
if (np>2000) np = 2000;
if (!spd) cleardevice();
spd=0;
// setlinestyle(DOTTED_LINE,0,1);
setcolor(co2);
rline3(-2,0,0,2,0,0); // draw axes
rline3(0,-2*zmag,0,0,2*zmag,0);
rline3(0,0,-2,0,0,2);
rline3(2.2,0,0,2,0,-0.1); // arrows
rline3(2.2,0,0,2,0, 0.1);
rline3(0,0,2.2,-0.1,0,2);
rline3(0,0,2.2, 0.1,0,2);
rline3(0,2.2*zmag,0,-0.1,2*zmag,0);
rline3(0,2.2*zmag,0, 0.1,2*zmag,0);
if (!kbhit()) {
setcolor(co1);
sprintf(ss,"%s %g ",msg[18],step );
if (polar==1) strcat(ss,msg[19]);
if (polar==2) strcat(ss,msg[37]);
strcat(ss,msg[36]);
outtextxy(0,0,ss); i=0;
sprintf(ss,"%s =[%8.4f,%8.4f] ",ivar[i].name,to,to+ts);
outtextxy(0,++i*10,ss);
sprintf(ss,"%s =[%8.4f,%8.4f] ",ivar[i].name,uo,uo+us);
outtextxy(0,++i*10,ss);
while (i<nv) {
sprintf(ss,"%s = %8.4f ",ivar[i].name,ivar[i].value);
outtextxy(0,++i*10,ss);
}
x = 180/M_PI;
sprintf(ss," %8.4f %8.4f %d ",zmag,focalen,np); outtextxy(0,++i*10,ss);
sprintf(ss," %8.4f %8.4f %8.4f ",cx,cy,cz); outtextxy(0,++i*10,ss);
sprintf(ss," %8.4f %8.4f %8.4f ",ax*x,ay*x,az*x); outtextxy(0,++i*10,ss);
if (mm) outtextxy(0,++i*10,msg[27]);
} else spd=1;
if (!kbhit()) {
setcolor(co2);
rline3(2,0,0.1,2,0,-0.1);
rline3(0.1,2*zmag,0,-0.1,2*zmag,0);
rline3(0.1,0,2,-0.1,0,2);
rline3(1,0,0.1,1,0,-0.1); // axis ticks
rline3(-1,0,0.1,-1,0,-0.1);
rline3(0.1,zmag,0,-0.1,zmag,0);
rline3(0.1,-1*zmag,0,-0.1,-1*zmag,0);
rline3(0.1,0,1,-0.1,0,1);
rline3(0.1,0,-1,-0.1,0,-1);
}
if (!kbhit() && !polar) {
setcolor(co3);
rline3(to ,0,uo ,to+ts,0,uo );
rline3(to+ts,0,uo ,to+ts,0,uo+us);
rline3(to+ts,0,uo+us,to ,0,uo+us);
rline3(to ,0,uo+us,to ,0,uo );
rline3(-1,-zmag, 1, 1,-zmag, 1); // draw a unit box
rline3( 1,-zmag, 1, 1, zmag, 1);
rline3( 1, zmag, 1,-1, zmag, 1);
rline3(-1, zmag, 1,-1,-zmag, 1);
rline3(-1,-zmag,-1,-1,-zmag, 1);
rline3( 1,-zmag,-1, 1,-zmag, 1);
rline3(-1, zmag,-1,-1, zmag, 1);
rline3( 1, zmag,-1, 1, zmag, 1);
rline3(-1,-zmag,-1, 1,-zmag,-1);
rline3( 1,-zmag,-1, 1, zmag,-1);
rline3( 1, zmag,-1,-1, zmag,-1);
rline3(-1, zmag,-1,-1,-zmag,-1);
}
// setlinestyle(SOLID_LINE,0,1);
setcolor(co1);
if (onp<np || onp>np*3) { onp = np;
if (save2) free(save2);
if (save3) free(save3);
save2 = malloc((np+1)*sizeof*save2);
save3 = NULL;
}
if (rain==2 && !save3) save3 = malloc((np+1)*sizeof*save3);
/*-----------------------
MAIN DRAW LOOP
*/
srand(2); cmn=32767; cmx=-cmn; // set color min/max
if (!kbhit())
for (j=0,x=to; j<=np; ++j,x+=ts/np) {
ivar[0].value = x;
for (i=0,y=uo; i<=np; ++i,y+=us/np) {
ivar[1].value = y;
xx = eval(fy); /* by default fy = ivar[0] */
zz = y;
yy = cc = eval(fz);
if (polar==1) { /* spherical */
x2 = xx;
xx = cos(y)*cos(x2)*yy;
zz = sin(y)*cos(x2)*yy;
yy *= sin(x2);
}
else if (polar==2) { /* cylindrical */
x2 = xx;
xx = yy*cos(y);
zz = yy*sin(y);
yy = x2;
}
yy *= zmag;
view3d(xx,yy,zz,&x2,&y2);
x2 = xz*(x2-xo);
y2 = yz*(ys+yo-y2);
c = ca*zmag*cc*np/ts; /* color or fill pattern */
if (c<cmn) cmn=c;
if (c>cmx) cmx=c;
if (rain==1) { /* color by height */
c = pmod(c,palsz)+palst;
setcolor(c);
}
else if (rain==2 && i && j) { /* color by derivative */
c = light_source(xx,yy,zz,save3+i-1,palsz) + palst;
setcolor(c);
}
if (fill) {
f.x3=save2[i].x2; f.y3=save2[i].y2;
f.x2=x2; f.y2=y2;
if (i && j) {
if (rain && fill>2) setcolor(7);
if (rain) setfillstyle(1,c);
else setfillstyle(fillpat[pmod(c,6)],co1);
if (fill&1) fillpoly(4,&f.x1);
else fillpoly(3,&f.x2);
}
f.x1=f.x2; f.y1=f.y2;
f.x4=f.x3; f.y4=f.y3;
}
else {
if (i) line(save2[i-1].x2,save2[i-1].y2,x2,y2);
if (j) line(save2[i].x2,save2[i].y2,x2,y2);
}
save2[i].x2 = x2;
save2[i].y2 = y2;
if (rain==2 && save3) {
save3[i].x = xx;
save3[i].y = yy;
save3[i].z = zz;
}
if (kbhit()) break;
}
if (kbhit()) break;
}
/* Draw a color legend at the bottom of the screen. */
if (rain && palsz>16 && !kbhit()) {
c = getmaxy();
for (i=0; i<256; ++i) { putpixel(i,c,i); putpixel(i,c-1,i); }
}
next_key:
if (rain && palrot && v256) while (!kbhit()) {
palix += palrot;
if (palst!=16 || palsz!=240) wave_palette();
palst=16; palsz=240;
c = clock();
set_palette(palix);
while (c <= clock() && c+2 > clock());
}
c = getch(); if (!c) c=getch(); // get ONE keystroke
while(kbhit()) c=getch(); // flush kbd buffer
if (c==27) return 1; /* escape */
if (c=='g') return 0; /* 2d graphics */
if (c=='d') np=0; // force reset above
if (c<84 && c>70) c+=mm;
switch (c) {
case 'c': rain=(rain+1)%3; break; // color mode
case 'x': // palette mode
if (!v256) break;
pp = 1-pp;
if (pp) wave_palette();
else gray_palette();
set_palette(palix);
if (palst==16 && palsz==240 && j>np) goto next_key;
palst=16; palsz=240;
break;
case '=': palrot -= (palrot>-10); goto next_key;
case '-': palrot += (palrot<10); goto next_key;
case 'z': if (cmx==cmn) ca=1.0; else ca*=1.0*palsz/(cmx-cmn); break;
case 'f': fill=(fill+1)%(5-!rain*2); break;
case 13: mm=200-mm; break;
case 75: spincam(3); break; // l,r,u,d
case 77: spincam(5); break;
case 72: spincam(1); break;
case 80: spincam(-1); break;
case ',': focalen*=rr;
case 82: cx*=rr; cy*=rr; cz*=rr; break; // ins,del
case '.': focalen/=rr;
case 83: cx/=rr; cy/=rr; cz/=rr; break;
case 71: np/=rr; break; // home end
case 79: if (np>3) np*=rr; break;
case 73: zmag/=rr; break; // pgup dn
case 81: zmag*=rr; break;
case 275: ay+=tt; break; // mm l,r,u,d
case 277: ay-=tt; break;
case 272: ax+=tt; break;
case 280: ax-=tt; break;
case 282: movecam(3); break; // mm ins,del
case 283: movecam(-3); break;
case 273: az+=tt; break; // mm pgup dn
case 281: az-=tt; break;
case 115: to-=ts/4; break; // ctrl l r u d
case 116: to+=ts/4; break;
case 141: uo+=us/4; break;
case 145: uo-=us/4; break;
case 146: ts/=rr; us/=rr;
to-=ts*rr1; uo-=us*rr1; break; // ctrl ins del
case 147: to+=ts*rr1; uo+=us*rr1;
ts*=rr; us*=rr; break;
case 132: us/=rr; uo-=us*rr1; break; // ctrl pgup dn
case 118: uo+=us*rr1; us*=rr; break;
case '?': cont-=.1; if (cont<0) cont=0; break; // f5
case '@': cont+=.1; if (cont>1) cont=1; break; // f6
case 'T': c='t';
default: handlekey(c);
if (c=='a')
if (polar) {
to=uo=-M_PI; ts=us=-2*uo;
if (polar==1) { to=uo/2; ts=us/2; }
}
}
}
/* never here */
}